package Service;



import java.sql.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author ale
 */
public class GestioneDB {    
    //Attributi
    private static Connection connessione = null;
    private static Boolean existTables=false;    
    private static GestioneDB instance=null;
    
    //Metodo Costruttore
    private GestioneDB(){ 
    
    }
    
    /*Pattern Singleton*/
    public static GestioneDB getInstance(){
        if (instance == null)
            instance =  new GestioneDB();
        return instance;
            
    }
    public void connetti(){
        try{ 
            /*Bisogna importare la libreria derby.jar da /usr/local/glassfish/javaDB/lib/*/
            Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
            connessione = DriverManager.getConnection("jdbc:derby://localhost:1527/MeteoDB;create=true");
        }catch(Exception e){
            System.err.println(e);
        }                 
    }

    public void disconnetti(){
        if (connessione!=null){
            try {
                connessione.close();
                connessione=null;
            } catch (SQLException ex) {
                System.err.println("Errore nella disconnessione!");
            }
        }
    }
    
    /*metodo che inizializza il database creando le tabelle se non esistono*/
    public void inizializzaDB(){
        /*mi connetto alla base di dati */
        this.connetti();        
        try{
            /* Controllo se la base di dati è già stata creata, in caso contrario la creo */
            boolean citta = false;
            boolean previsioni = false;
            boolean regioni = false;
            boolean utenti = false;
            ResultSet rsTabelle = connessione.getMetaData().getTables(null, null, null, null);            
            /*scorro tutte le tabelle*/
            while (rsTabelle.next()) {
                /* controllo che le tabelle esistano, riporto tramite una flag quelle non ancora create*/
                if (rsTabelle.getString("TABLE_NAME").compareToIgnoreCase("citta") == 0) {
                    citta = true;
                }
                if (rsTabelle.getString("TABLE_NAME").compareToIgnoreCase("previsioni") == 0) {
                    previsioni = true;
                }
                if (rsTabelle.getString("TABLE_NAME").compareToIgnoreCase("regioni") == 0) {
                    regioni = true;
                }
                if (rsTabelle.getString("TABLE_NAME").compareToIgnoreCase("utenti") == 0) {
                    utenti = true;
                }
            }
           
           /* Nel caso non sia stata trovata una tabella allora è necessario crearla*/
           if (!regioni) {
                String sql1 = "CREATE TABLE Regioni "
                            + "(Id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),"                            
                            + " Nome VARCHAR(30) NOT NULL,"
                            + " PRIMARY KEY (Nome))";                            
                Statement ps = connessione.createStatement();
                ps.executeUpdate(sql1);
                System.err.println("Tabella regioni creata con successo.");
                ps.close();
           }
           if (!citta) {
                String sql2 =  "CREATE TABLE Citta "
                            + "(Id INTEGER NOT NULL,"                            
                            + " Nome VARCHAR(60) NOT NULL,"
                            + " Sigla VARCHAR(2) NOT NULL,"
                            + " Regione VARCHAR(30) NOT NULL,"
                            + " PRIMARY KEY (Id),"
                            + " FOREIGN KEY (Regione) REFERENCES Regioni(Nome))";
                Statement ps = connessione.createStatement();
                ps.executeUpdate(sql2);
                System.err.println("Tabella citta creata con successo.");
                ps.close();
           }
           if (!previsioni) {
                String sql3 =  "CREATE TABLE Previsioni "
                            + "(Id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),"                            
                            + " Data DATE NOT NULL,"
                            + " Previsione VARCHAR(40) NOT NULL,"
                            + " Tmin VARCHAR(10) NOT NULL,"
                            + " Tmax VARCHAR(10) NOT NULL,"
                            + " Precipitazioni VARCHAR(10) NOT NULL,"
                            + " Vento VARCHAR(20) NOT NULL,"
                            + " Pressione VARCHAR(10) NOT NULL,"
                            + " Quotazero VARCHAR(10) NOT NULL,"
                            + " IdCitta INTEGER NOT NULL,"
                            + " PRIMARY KEY (Id),"
                            + " FOREIGN KEY (IdCitta) REFERENCES citta(Id))";
                Statement ps = connessione.createStatement();
                ps.executeUpdate(sql3);
                System.err.println("Tabella previsioni creata con successo.");
                ps.close();
           }           
           if (!utenti){
               String sql4 =  "CREATE TABLE Utenti "
                            + "(Id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),"                            
                            + " Username VARCHAR(20) NOT NULL,"
                            + " Password VARCHAR(20) NOT NULL,"
                            + " PRIMARY KEY (Id))";
                Statement ps = connessione.createStatement();
                ps.executeUpdate(sql4);
                System.err.println("Tabella utenti creata con successo.");
                ps.close();
           }
           existTables=true;
           this.disconnetti();           
       } catch (Exception e) {
           System.err.println("Errore nella creazione delle tabelle!"+e);
       }
    }
    
    /*-------------------------------------------------------------------------------------------------*/
    /*--------------------PROCEDURE PER SALVARE LE CITTA' DA CSV----------------------------------------*/
    /*--------------------------------------------------------------------------------------------------*/
    public int getIdCitta(String citta){
        int ret= -1;
        connetti();
        try{    
            PreparedStatement ps = connessione.prepareStatement("SELECT Id FROM Citta  WHERE Nome=?");
            ps.setString(1, citta);
            ResultSet res = ps.executeQuery();
            while (res.next())
                ret = res.getInt("Id");
            //System.out.println(ret);
            ps.close();            
        } catch (SQLException ex) {
            System.err.println(ex);
            System.out.println("Peccato! :-(");
            
        }
        disconnetti();
        return ret;
    }
    
    /*funzione che inserisce nel database una città*/
    public synchronized void inserisciCitta(int id, String nome, String provincia, String regione){
       connetti();
        try{                
            PreparedStatement ps = connessione.prepareStatement("INSERT INTO citta (Id,Nome,Sigla,Regione) VALUES (?,?,?,?)");
            ps.setInt(1,id);
            ps.setString(2,nome);
            ps.setString(3,provincia);
            ps.setString(4,regione);
            ps.executeUpdate();
            ps.close();            
        } catch (SQLException ex) {
            System.out.println("Errore nell'inserimento delle città nel db "+ nome);
        }
        disconnetti();
    }
    /**
     * @param city il nome della città
     * @param offset i giorni di scarto dalla data odierna di cui si vuole avere previsione
     * @return 
     */
    public Previsione previsioneCitta(String city, int offset){
        Previsione ret;
        int idCitta=getIdCitta(city);
        Calendar date= new GregorianCalendar();
        date.add(Calendar.DAY_OF_MONTH, offset);
        Date data = new Date(date.getTimeInMillis());
        ret=new Previsione(-1,city,date);
        connetti();
        
        try{    
            PreparedStatement ps = connessione.prepareStatement("SELECT * FROM Previsioni WHERE IdCitta=? AND Data=?");
            ps.setInt(1, idCitta);
            ps.setDate(2, data);
            ResultSet res = ps.executeQuery();
            res.next();
                {
                ret.setId(res.getInt("IdCitta"));
                ret.setPrevisione(res.getString("Previsione"));
                ret.settMin(res.getString("Tmin"));
                ret.settMax(res.getString("Tmax"));
                ret.setPercentualePrecipitazioni(res.getString("Precipitazioni"));
                ret.setVentoKmh(res.getString("Vento"));
                ret.setPressione(res.getString("Pressione"));
                ret.setQuotaZero(res.getString("Quotazero"));
                }
            ps.close();            
        } catch (SQLException ex) {
            System.err.println(ex);
            System.out.println("Peccato! :-(");
            disconnetti();
            return null;
        }
        disconnetti();
        return ret;
    }
    
    /*Cerca una città nel db e restituisce true o false se esiste*/
    public boolean esisteCitta(int id,String nome){       
       boolean trovato=false;
       connetti();
       try{    
            PreparedStatement ps = connessione.prepareStatement("SELECT * FROM Citta WHERE Id=?");
            ps.setInt(1, id);            
            ResultSet res = ps.executeQuery();
            while(res.next())
                trovato=true;
            ps.close();            
        } catch (SQLException ex) {
            System.err.println("Errore nella ricerca della regione");
            System.out.println("--"+nome+"--");
        }       
       disconnetti();
        return trovato;
    }
    
    /*funzione che inserisce nel database una regione*/
    public void inserisciRegione(String regione){       
       connetti();
        try{    
            PreparedStatement ps = connessione.prepareStatement("INSERT INTO Regioni (Nome) VALUES (?)");
            ps.setString(1, regione);
            ps.executeUpdate();
            ps.close();            
        } catch (SQLException ex) {
            System.err.println("Errore nell'inserimento della regione nel db -- "+regione+"");
        }       
        disconnetti();
    }
    
    public boolean esisteRegione(String regione){       
       boolean trovato=false;
       connetti();
      try{    
            PreparedStatement ps = connessione.prepareStatement("SELECT * FROM Regioni WHERE Nome=?");
            ps.setString(1, regione);
            ResultSet res = ps.executeQuery();
            if(res.next())
                trovato=true;                
            ps.close();            
        } catch (SQLException ex) {
            //System.err.println("Errore nella ricerca della regione");
            //System.out.println("--"+regione+"--");
        }        
        disconnetti();
        return trovato;
    }
    
    /*-------------------------------------------------------------------------------------------------*/
    /*--------------------PROCEDURE PER GESTIONE DELLE PREVISIONI----------------------------------------*/
    /*--------------------------------------------------------------------------------------------------*/
    
    public void inserisciPrevisione(Previsione prev){
        connetti();
        try{                
            PreparedStatement ps = connessione.prepareStatement("INSERT INTO Previsioni (Data,Previsione,Tmin,Tmax,Precipitazioni,Vento,Pressione,Quotazero,IdCitta) VALUES (?,?,?,?,?,?,?,?,?)");
            Calendar cal = prev.getDataPrevisione();
            Date x = new Date(cal.getTimeInMillis());
            ps.setDate(1,x);
            ps.setString(2,prev.getPrevisione());
            ps.setString(3,prev.gettMin());
            ps.setString(4,prev.gettMax());
            ps.setString(5,prev.getPercentualePrecipitazioni());
            ps.setString(6,prev.getVentoKmh());
            ps.setString(7,prev.getPressione());
            ps.setString(8,prev.getQuotaZero());
            ps.setInt(9, prev.getId());
            ps.executeUpdate();
            ps.close();  
            System.out.println("previsione inserita\n"+prev);
        } catch (SQLException ex) {
            //System.out.println("Errore nell'inserimento della previsione del...............................nel db ");
            //System.err.println(ex);
        }
        disconnetti();
    }
    public String[] getCitta(){
        ArrayList<String> ret = new ArrayList();
        connetti();
        try{
            PreparedStatement ps = connessione.prepareStatement("SELECT Nome FROM Citta");
            ResultSet rs = ps.executeQuery();
            while(rs.next())
                ret.add(rs.getString("Nome"));
        }catch(SQLException e){
            System.err.println("Aiuto" + e);
        }
        disconnetti();
        String[] r = new String[ret.size()];
        ret.toArray(r);
        return r;
    }
    
    /**
     * Metodo per controllare se una previsione esiste già
     */
    public boolean esistePrevisione(Previsione p){
        connetti();
        boolean trovata = false;
        Calendar cal = p.getDataPrevisione();
        Date x = new Date(cal.getTimeInMillis());
        try{
            PreparedStatement ps = connessione.prepareStatement("SELECT * FROM Previsioni WHERE IdCitta=? AND Data=?");
            ps.setInt(1,p.getId());
            ps.setDate(2,x);
            ResultSet rs = ps.executeQuery();
            if(rs.next())
                trovata=true;
        }catch(SQLException e){
            //System.err.println("Errore nella query - EsistePrevisione();");
        }
        disconnetti();
        return trovata;
    }
    /**
     * aggiorna il database a partire da un determinato indice di città;
     * @param i l'indice dal quale parte
     * @throws Exception eccezioni nel parsing;
     */
    public void aggiornaPrevisioniDB(int i)throws Exception {
        connetti();
        System.out.println("Inizio aggiornamento");
        PreparedStatement ps = connessione.prepareStatement("SELECT max(Id) as Id FROM Citta");
       
        ResultSet rs = ps.executeQuery();
        rs.next();
        int maxId=rs.getInt("Id");
        disconnetti();
        int numeroErrori=-1;
        
        while (i<maxId && Main.isRunning()){
            
            i=auxAggiornaPrevisioniDB(i);
            
            numeroErrori++;
            
            }
        System.out.println("Numero errori: "+numeroErrori);
   }
    public void aggiornaPrevisioniDB()throws Exception{
        aggiornaPrevisioniDB(0);
    }
   
    private synchronized int auxAggiornaPrevisioniDB(int id)throws Exception {
        connetti();
        PreparedStatement ps = connessione.prepareStatement("SELECT Id FROM Citta WHERE id>=? ORDER BY Id");
        ps.setInt(1,id);
        ResultSet rs = ps.executeQuery();
        Previsione[] p;
        try{
        while (rs.next() && Main.isRunning()){//TODO Togliere quello schifo di thread
            id=rs.getInt("Id");
            p = ParsingHtml.estraiPrevisioneLocalita(id);
            for(int i=0; i < 7; i++){
                if (!esistePrevisione(p[i])){
                    inserisciPrevisione(p[i]);
                }else{
                    aggiornaPrevisione(p[i]);
                }
            }            
         }
         }catch(Exception e){
                wait(2000);
                System.out.println("Errore nel parsing all'Id "+id+": "+e.getMessage());
                }
        disconnetti();
        return id;
   }
    /**
     * La funzione aggiorna una previsione nel database.
     * @param prev Previsione
     * @return Sovrascrive a quella già esistente
     */
    public void aggiornaPrevisione(Previsione prev){
        Calendar cal = prev.getDataPrevisione();
        Date x = new Date(cal.getTimeInMillis());
        connetti();
        try{
            PreparedStatement ps = connessione.prepareStatement("UPDATE Previsioni SET Pressione=?, Previsione=?, Precipitazioni=?, Vento=?, Tmin=?, Tmax=?, Quotazero=? WHERE IdCitta=? AND Data=?");
            ps.setDate(9,x);
            ps.setString(2,prev.getPrevisione());
            ps.setString(5,prev.gettMin());
            ps.setString(6,prev.gettMax());
            ps.setString(3,prev.getPercentualePrecipitazioni());
            ps.setString(4,prev.getVentoKmh());
            ps.setString(1,prev.getPressione());
            ps.setString(7,prev.getQuotaZero());
            ps.setInt(8, prev.getId());
 
            ps.executeUpdate();
            System.out.println("aggiornamento eseguito\n"+ prev);
        }catch(SQLException e){
            System.out.println("Errore nell'aggiornamento delle previsioni");
            System.err.println(e);
        }
        disconnetti();
    }
    /*-------------------------------------------------------------------------------------------------*/
    /*--------------------PROCEDURE LA PRELEVARE I DATI DAL DB-----------------------------------------*/
    /*--------------------------------------------------------------------------------------------------*/
    
    /*Utenti*/
    public Utente login(String user, String pass)  {
        this.connetti();
        /*controllo che esista la tabella utenti e sia stata effettuata la connessione*/ 
        try{
            /*Cerco l'utente*/
            PreparedStatement ps = connessione.prepareStatement("SELECT * FROM Utenti WHERE Username=? AND Password=?");
            ps.setString(1, user);
            ps.setString(2, pass);
            ResultSet res = ps.executeQuery();

            /* Se ho un risultato allora ho trovato l'utente*/
            while(res.next()){
                    
                    Utente utente = new Utente(res.getString("Username"), res.getString("Password"));
                    this.disconnetti();
                     return utente;
            }              
        }catch(SQLException e){
            System.out.println("Query login() - "+e);
        }
        this.disconnetti();
        return null;
    }
    
    public boolean esisteUtente(String user){ 
        this.connetti();    
        try{
            /*Cerco l'utente*/
            PreparedStatement ps = connessione.prepareStatement("SELECT * FROM Utenti WHERE Username=?");
            ps.setString(1, user);
            ResultSet res = ps.executeQuery();
            
            /* Se ho un risultato allora ho trovato l'utente*/
            while(res.next()){
                this.disconnetti();
                return true;
            }
           //Utente non trovato                    
        }catch(SQLException e){
            System.out.println("Query esisteUtente() - "+e);
        }    
        this.disconnetti();
        return false;
    }
    
    public boolean aggiungiUtente(String user, String pass){ 
        this.connetti();
        try{
            
                /*Inserisco l'utente*/
                PreparedStatement ps = connessione.prepareStatement("INSERT INTO Utenti (Username,Password) VALUES (?,?)");
                ps.setString(1, user);
                ps.setString(2, pass);
                ps.executeUpdate();
                this.disconnetti();
                return true;
         
        }catch(SQLException e){
            System.out.println("Query inserisciUtente() - "+e);
        }
        this.disconnetti();
        return false;
    }
    
    public Boolean cambiaPassword(String username, String password){
        this.connetti();
        try{    
            /*Inserisco l'utente*/
            PreparedStatement ps = connessione.prepareStatement("UPDATE Utenti SET Password = ? WHERE Username = ?");
            ps.setString(1, password);
            ps.setString(2, username);
            ps.executeUpdate();
            this.disconnetti();
            return true;      
        }catch(SQLException e){
            System.out.println("Query cambiapassword() - "+e);
        }
        this.disconnetti();
        return false;
    }
    /*-------------------------------------------------------------------------------------------------*/
    /*--------------------PROCEDURE PER LA STAMPA DEL DATABASE----------------------------------------*/
    /*--------------------------------------------------------------------------------------------------*/
    
    public void stampaRegioni(){
        connetti();
        try{    
            PreparedStatement ps = connessione.prepareStatement("SELECT * FROM Regioni");
            ResultSet res = ps.executeQuery();
            System.out.println("----------------------------------------------");
            System.out.println("---------------REGIONI------------------------");
            System.out.println("----------------------------------------------");
            while(res.next()){
                System.out.println("---"+res.getInt("Id")+" - "+res.getString("Nome")+"");
            }
            System.out.println("----------------------------------------------");
            ps.close();
         } catch (SQLException ex) {
            System.err.println("Errore nella ricerca della regione");            
        }
        disconnetti();
    }
    public void stampaCitta(){
        try{            
            connetti();
            PreparedStatement ps = connessione.prepareStatement("SELECT * FROM Citta");
            ResultSet res = ps.executeQuery();
            System.out.println("----------------------------------------------");
            System.out.println("-----------------CITTA------------------------");
            System.out.println("----------------------------------------------");
            while(res.next()){
                System.out.println("---"+res.getInt("Id")+" - "+res.getString("Nome")+" - "+res.getString("Sigla")+" - "+res.getString("Regione")+"");
            }
            System.out.println("----------------------------------------------");
            ps.close();
            
        } catch (SQLException ex) {
            System.err.println("Errore nella ricerca della città");            
        }
        disconnetti();
    }
  public void stampaPrevisioni(){
        try{          
            connetti();
            PreparedStatement ps = connessione.prepareStatement("SELECT * FROM Previsioni");
            ResultSet res = ps.executeQuery();
            System.out.println("----------------------------------------------");
            System.out.println("---------------PREVISIONI----------------------");
            System.out.println("----------------------------------------------");
            while(res.next()){
                System.out.println("---"+res.getInt("Id")+";\t"+res.getInt("IdCitta")+";\t"+res.getDate("Data")+";\t"+res.getString("Previsione")+"\t\t"+res.getString("Tmin")+";\t"+res.getString("Tmax")+";\t"+res.getString("Precipitazioni")+";\t"+res.getString("Vento")+";\t"+res.getString("Pressione")+";\t"+res.getString("Quotazero"));
            }
            System.out.println("----------------------------------------------");
            ps.close();
            
        } catch (SQLException ex) {
            System.err.println("Errore nella ricerca della previsione");            
        }
        disconnetti();
    } 
    public static void main(String[] args){
        GestioneDB d = GestioneDB.getInstance();
        d.inizializzaDB();
        /* LettoreCsv l = new LettoreCsv();
        l.LeggiCsv();
        d.stampaRegioni();
        d.stampaCitta();*/
    //        String[] citta = d.getCitta();
    //         for(int i=0; i<citta.length; i++)
    //                    System.out.println(citta[i]+",");
        try{
        d.aggiornaPrevisioniDB(3420);
        }catch(Exception e){
        e.printStackTrace();
        }
        /*ResultSet res = d.getPrevisioniFromCitta("roma");
        System.out.println("Res:"+res);*/
//try {
//            /* Prova connessioneDB*/
//           Main m = new Main();
//           m.start();
//        
//           //Thread.sleep(5000);
//           m.stopThread();
//           //Thread.sleep(5000);
//           System.out.println(m.getLog());
//        } catch (Exception ex) {
//            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
//        }
    }
}
